package com.logic.modular.game.player.match.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.comment.exception.ServiceException;
import com.logic.comment.util.*;
import com.logic.config.netty.web.service.MessageService;
import com.logic.modular.game.battle.info.base.PvpPlayerInfo;
import com.logic.modular.game.battle.info.base.PvpPlayerInfoServiceImpl;
import com.logic.modular.game.core.service.MatchEndServiceImpl;
import com.logic.modular.game.player.bag.service.PlayerPropType1ServiceImpl;
import com.logic.modular.game.player.info.service.PlayerStatusServiceImpl;
import com.comment.pojo.domain.logic.domain.Player;
import com.comment.pojo.domain.logic.domain.PlayerStatus;
import com.logic.modular.game.player.info.service.PlayerServiceImpl;
import com.logic.modular.game.PvpCity;
import com.logic.modular.game.player.match.service.base.PvpCityBaseServiceImpl;
import com.logic.modular.gameCore.map.CityGraph;
import com.comment.pojo.domain.logic.core.CoreCity;
import com.comment.pojo.domain.logic.core.CoreCityLink;
import com.logic.modular.gameCore.map.service.CoreCityLinkServiceImpl;
import com.logic.modular.gameCore.map.service.CoreCityServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.Resource;

/**
 * 城池信息(PvpCity)表服务实现类
 *
 * @author cxy
 * @since 2024-07-22 15:13:38
 */
@Slf4j
@Service

public class PvpCityServiceImpl extends PvpCityBaseServiceImpl {
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private HashOperations<Object, Object, Object> hashOperations;
    @Resource
    private CoreCityLinkServiceImpl coreCityLinkService;
    @Resource
    private PvpPlayerInfoServiceImpl pvpPlayerInfoServiceImpl;
    @Resource
    private PlayerStatusServiceImpl playerMatchServiceImpl;
    @Resource
    private PlayerServiceImpl playerServiceImpl;
    @Resource
    private CoreCityLinkServiceImpl coreCityLinkServiceImpl;
    @Resource
    private PlayerStatusServiceImpl playerStatusServiceImpl;
    @Resource
    private PlayerPropType1ServiceImpl playerPropType1ServiceImpl;
    @Resource
    private MatchEndServiceImpl matchEndService;

    public List<PvpCity> list(Long playerId, Long ownerUid, Long coreCityId, Integer type) {
        PlayerStatus playerStatus = playerStatusServiceImpl.obtainPlayerStatus(playerId);
        Long currentMatchId = playerStatus.getCurrentMatchId();
        if (currentMatchId < 1) {
            throw new ServiceException("当前没有对局");
        }
        return listCache(currentMatchId).stream().filter(city -> BeanUtil.selector()
                .eq(ownerUid, city.getOwnerUid())
                .eq(coreCityId, city.getCoreCityId())
                .eq(type, city.getType())
                .value()).collect(Collectors.toList());
    }

    public void initPvpPlayerCity(Long uid, PvpCity city) {
        Player userInfo = playerServiceImpl.select(uid);
        CoreCity coreCity = CoreCityServiceImpl.getCache(city.getCoreCityId());
        city.setOwnerUid(uid);
        city.setOwnerName(userInfo.getNickname());
        city.setStatus(1);
        city.setReadyArmy(coreCity.getDefArmy().longValue());
        update(city);
    }

    public CoreCityLink bfsAllPaths(Integer startCityId, Integer targetCityId) {
        List<CoreCityLink> cityLinks = coreCityLinkService.listCache();
        Graph graph = CityGraph.initGraph(cityLinks);
        Set<Set<Node>> sets = graph.bfsAllPaths(startCityId, targetCityId);
        if (sets.isEmpty()) {
            throw new ServiceException("没有可用路线");
        }

        // 获取路线
        CoreCityLink dto = CoreCityLink.builder()
                .startCityId(startCityId)
                .targetCityId(targetCityId)
                .needTime(0)
                .build();

        List<CoreCityLink> routes = new ArrayList<>();// 路线集合
        sets.forEach(nodes -> {// 路线
            List<CoreCityLink> links = new ArrayList<>();// 路线的节点集合

            CoreCityLink child = new CoreCityLink();// 路线，节点集合

            List<Node> nodeList = new ArrayList<>(nodes);//
            child.setNeedTime(0);
            child.setStartCityId(nodeList.get(0).getA());
            child.setTargetCityId(nodeList.get(nodeList.size() - 1).getB());
            nodeList.stream().map(node -> this.filter(      // 节点
                    node, cityLinks
            )).forEach(filter -> {
                child.setNeedTime(filter.getNeedTime() + child.getNeedTime());
                links.add(filter);
            });
            child.setMapCellLinkList(links);
            routes.add(child);
        });
        dto.setMapCellLinkList(routes);
        return dto;
    }

    public Node bfsOwnerMinPaths(Long playerId, Integer startCityId, Integer targetCityId) {
        PlayerStatus playerMatch = playerMatchServiceImpl.get(playerId);
        Long currentMatchId = playerMatch.getCurrentMatchId();
        PvpPlayerInfo pvpPlayerInfo = pvpPlayerInfoServiceImpl.get(currentMatchId, playerId);
        Set<Long> currentCity = pvpPlayerInfo.getCurrentCity();


        List<CoreCityLink> cityLinks = coreCityLinkService.listCache();

        List<CoreCityLink> collect = cityLinks.stream()
                .filter(value -> currentCity.contains(Long.valueOf(value.getStartCityId()))
                ).collect(Collectors.toList());

        Map<String, Integer> costMap = collect.stream().collect(Collectors.toMap(
                k -> k.getStartCityId() + "-" + k.getTargetCityId(),
                CoreCityLink::getNeedTime
        ));


        Graph graph = CityGraph.initGraph(collect);
        Set<Set<Node>> allPaths = graph.bfsAllPaths(Math.toIntExact(startCityId), targetCityId, costMap);
        if (allPaths == null || allPaths.isEmpty()) {
            throw new ServiceException("没有可用路线");
        }
        Set<Node> pathAndCostSet = new HashSet<>();
        for (Set<Node> path : allPaths) {// 路线
            Node pathAndCost = new Node();
            pathAndCost.setA(startCityId);
            pathAndCost.setB(targetCityId);
            pathAndCost.setLink(path);
            pathAndCost.setCos(path.stream().mapToInt(Node::getCos).sum());
            pathAndCostSet.add(pathAndCost);
        }
        return pathAndCostSet.stream().min(Comparator.comparingInt(Node::getCos)).get();
    }

    public CoreCityLink bfsOwnerPaths(Long startCityId, Long targetCityId, Long matchId, Long playerId) {
        List<CoreCityLink> ownerCityLinks;
        // 筛选出城池路线 pvpMatch:53:city
        ownerCityLinks = coreCityLinkService.listCache().stream().filter(item -> {
            return hashOperations.entries("pvpMatch:" + matchId + ":city").values(
            ).stream().map(obj -> {
                PvpCity city = (PvpCity) obj;
                return city.getOwnerUid();
            }).filter(item1 ->
                    item1.equals(playerId)
            ).collect(Collectors.toList()).contains(item.getStartCityId());
        }).collect(Collectors.toList());


        Graph graph = CityGraph.initGraph(ownerCityLinks);
        Set<Set<Node>> sets = graph.bfsAllPaths(Math.toIntExact(startCityId), Math.toIntExact(targetCityId));
        if (sets.isEmpty()) {
            log.error("没有可用路线");
            throw new ServiceException("没有可用路线");
        }


        // 获取路线
        CoreCityLink dto = CoreCityLink.builder()
                .startCityId(Math.toIntExact(startCityId))
                .targetCityId(Math.toIntExact(targetCityId))
                .needTime(0)
                .build();

        List<CoreCityLink> routes = new ArrayList<>();// 路线集合
        sets.forEach(nodes -> {// 路线
            List<CoreCityLink> links = new ArrayList<>();// 路线的节点集合

            CoreCityLink child = new CoreCityLink();// 路线，节点集合

            List<Node> nodeList = new ArrayList<>(nodes);//
            child.setNeedTime(0);
            child.setStartCityId(nodeList.get(0).getA());
            child.setTargetCityId(nodeList.get(nodeList.size() - 1).getB());
            nodeList.stream().map(node -> this.filter(      // 节点
                    node, ownerCityLinks
            )).forEach(filter -> {
                child.setNeedTime(filter.getNeedTime() + child.getNeedTime());
                links.add(filter);
            });
            child.setMapCellLinkList(links);
            routes.add(child);
        });
        dto.setMapCellLinkList(routes);
        return dto;
    }


    public CoreCityLink filter(Node node, List<CoreCityLink> mapCellLinks) {
        CoreCityLink mapCellLinkDto = new CoreCityLink();
        CoreCityLink mapCellLink1 = mapCellLinks.stream().filter(mapCellLink -> mapCellLink.getStartCityId() == node.getA() && mapCellLink.getTargetCityId() == node.getB()).collect(Collectors.toList()).get(0);
        mapCellLinkDto.setStartCityId(mapCellLink1.getStartCityId());
        mapCellLinkDto.setTargetCityId(mapCellLink1.getTargetCityId());
        mapCellLinkDto.setNeedTime(mapCellLink1.getNeedTime());
        return mapCellLinkDto;
    }


    public PvpCity getPlayerCity(Long playerId, Long targetCityId, Long currentMatchId) {

        String cityKey = "pvpMatch:" + currentMatchId + ":city";
        PvpCity pvpCity = (PvpCity) hashOperations.get(cityKey, targetCityId + "");
        if (pvpCity == null) {
            throw new ServiceException("城池不存在");
        }
        if (!Objects.equals(pvpCity.getOwnerUid(), playerId)) {
            throw new ServiceException("城池不属于你");
        }
        return pvpCity;
    }

    /**
     * 玩家占领城池
     */
    public void playerOccupyTheCity(
            Long currentMatchId,
            Long ownerUid,
            Long coreCityId,
            Long winnerPlayerId
    ) {
        pvpPlayerInfoServiceImpl.playerOccupyTheCity(currentMatchId, ownerUid, coreCityId, winnerPlayerId);
        matchEndService.tryMilitaryConquestSettlement(currentMatchId);
    }

    public PvpCity getPvpCity(Long defenseCityId, Long currentMatchId) {
        List<PvpCity> cities = listCache(
                currentMatchId
        ).stream().filter(item ->
                Objects.equals(item.getCoreCityId(), defenseCityId)
        ).collect(Collectors.toList());
        if (cities.isEmpty()) {
            throw new ServiceException("没有找到城池");
        }
        return cities.get(0);
    }

    /**
     * 清除玩家 所有城市|以及刷新玩家信息
     */
    public void clearAllPlayerCities(PvpPlayerInfo pvpPlayerInfo) {
        Set<Long> currentCity = pvpPlayerInfo.getCurrentCity();
        if (!currentCity.isEmpty()) {
            currentCity.stream()
                    .map(coreCityId -> getCache(pvpPlayerInfo.getMatchId(), coreCityId))
                    .forEach(this::clearProductivity);
        }
    }

    /**
     * 清除 玩家|生产力统计
     */
    public void clearProductivity(PvpCity pvpCity) {
        CoreCity coreCity = CoreCityServiceImpl.getCache(pvpCity.getCoreCityId());
        pvpCity.setGrainRate(coreCity.getGrainRate());
        pvpCity.setCopperRate(coreCity.getCopperRate());
        pvpCity.setReadyArmyRate(coreCity.getReadyArmyRate());
        pvpCity.setReadyArmy(0L);
        pvpCity.setOwnerUid(0L);
        pvpCity.setOwnerName("");
        savaCache(pvpCity);
    }
    public List<PvpCity> obtainCanAttackTheCityWalls(
            Long robotPlayerId, Long matchId
    ) {
        PvpPlayerInfo pvpPlayerInfo = pvpPlayerInfoServiceImpl.get(matchId, robotPlayerId);
        Set<Long> currentCity = pvpPlayerInfo.getCurrentCity();
        // 他人城池
        List<PvpCity> allOtherCity = listCache(matchId).stream()
                .filter(item -> !currentCity.contains(item.getCoreCityId()))
                .collect(Collectors.toList());
        // 拥有的路线
        List<CoreCityLink> hasLink = coreCityLinkServiceImpl.list(new LambdaQueryWrapper<CoreCityLink>().in(CoreCityLink::getStartCityId, currentCity));
        // 路线可达的城池id
        Set<Long> collect = hasLink.stream().mapToLong(CoreCityLink::getTargetCityId).boxed().collect(Collectors.toSet());
        // 路线可可达他人城池
        return allOtherCity.stream().filter(item -> collect.contains(item.getCoreCityId())).collect(Collectors.toList());
    }

    @Resource
    private XSThreadUtil xsThreadUtil;

    /**
     * 运输兵力
     */
    public void transport(Long playerId, Long startCityId, Long targetCityId, Integer troopsQuantity) {
        PlayerStatus playerStatus = playerStatusServiceImpl.get(playerId);
        Long currentMatchId = playerStatus.getCurrentMatchId();
        if (currentMatchId == null || currentMatchId == 0L) {
            throw new ServiceException("当前不在对局中");
        }
        PvpCity playerCity = getPlayerCity(playerId, startCityId, currentMatchId);
        if (playerCity.getStatus() != 1) {
            throw new ServiceException("当前城池处于非正常状态");
        }
        Long readyArmy = playerCity.getReadyArmy();
        if (readyArmy < troopsQuantity) {
            throw new ServiceException("当前城池没有这么多兵力");
        }
        long need = troopsQuantity * 5L;
        if (!playerPropType1ServiceImpl.tryPay(playerId, 5L, BigDecimal.valueOf(need))) {
            throw new ServiceException("粮草不足");
        }

        PvpCity targetCity = getPlayerCity(playerId, targetCityId, currentMatchId);
        if (targetCity == null) {
            throw new ServiceException("目标城池不存在");
        }

        Node node = bfsOwnerMinPaths(playerId, Math.toIntExact(startCityId), Math.toIntExact(targetCityId));
        if (node == null) {
            throw new ServiceException("没有找到路径");
        }

        node.setRankNo(-1);
        node.setPlayerId(playerId);
        MessageService.sendMessageToUser(playerId, MessageService.updateJsonObject("transport", node));

        playerCity.setReadyArmy(readyArmy - troopsQuantity);
        update(playerCity);

        int cos = node.getCos();// 秒
        xsThreadUtil.scheduledExecutorService().schedule(() -> {
            targetCity.setReadyArmy(targetCity.getReadyArmy() + troopsQuantity);
            update(targetCity);
        }, cos, TimeUnit.SECONDS);
    }

    public List<PvpCity> listMyCity(Long playerId) {
        Long currentMatchId = playerStatusServiceImpl.currentMatchId(playerId);

        PvpPlayerInfo pvpPlayerInfo = pvpPlayerInfoServiceImpl.get(currentMatchId, playerId);
        if (pvpPlayerInfo == null) {
            throw new ServiceException("当前对局不存在");
        }
        if (pvpPlayerInfo.getCurrentCity().isEmpty()) {
            throw new ServiceException("你的城池已丢完");
        }
        return pvpPlayerInfo.getCurrentCity().stream().map(
                cityId -> getCache(currentMatchId, cityId)).collect(Collectors.toList());
    }

    public List<PvpCity> listEnemyCity(Long playerId) {
        Long currentMatchId = playerStatusServiceImpl.currentMatchId(playerId);
        return listCache(currentMatchId).stream()
                .filter(city -> city.getOwnerUid() != null // 排除空
                        && city.getOwnerUid() != 0L // 排除中立
                        && !city.getOwnerUid().equals(playerId)// 排除自己
                )
                .collect(Collectors.toList());
    }


}
